﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;

namespace Activity.III
{
	/// <summary>
	/// Демонстрационная программа 3-ей лабораторной работы
	/// </summary>
	public class Program
	{
		public const int DefaultSize = 10; //						Размер списка по умолчанию
		protected static Random random = new Random(); //			Генератор случайных чисел
		protected static LinkedList buffer = new LinkedList(); //	Линейный список

		/// <summary>
		/// Начало программы.
		/// </summary>
		public static void Main ()
		{
			Console.BufferHeight = 1000;
			Console.OutputEncoding = Encoding.Unicode;
			buffer = new LinkedList(DefaultSize);
			Fill(buffer);
			DisplayMenu(MenuEntry.Main);
		}

		/// <summary>
		/// Отобразить указанное меню.
		/// </summary>
		/// <param name="menu">Числовое представление активного меню.</param>
		public static void DisplayMenu (MenuEntry menu)
		{
			Console.Clear();
			Console.WriteLine("Лабораторная работа #3 [Вариант #5]");
			Console.WriteLine("Тема: \"Работа с линейными списками\"\n");
			// Переходим к следующему меню
			switch (menu)
			{
				case MenuEntry.Main:
					MainMenu();
					break;
				case MenuEntry.Create:
					CreateArrayMenu();
					break;
				case MenuEntry.Fill:
					FillArrayMenu();
					break;
				case MenuEntry.Display:
					PrintArrayMenu();
					break;
				case MenuEntry.Sort:
					SortArrayMenu();
					break;
				case MenuEntry.Аdd:
					AddArrayMenu();
					break;
				case MenuEntry.Remove:
					RemoveArrayMenu();
					break;
				case MenuEntry.Exit:
					// Не переходим к следующему меню
					return;
			}
		}

		/// <summary>
		/// Отобразить главное меню.
		/// </summary>
		private static void MainMenu ()
		{
			Console.WriteLine("Главное Меню");
			Console.WriteLine();
			Console.WriteLine("1. Создать список");
			Console.WriteLine("2. Заполнить список");
			Console.WriteLine("3. Показать список");
			Console.WriteLine("4. Сортировка списка");
			Console.WriteLine("5. Добавление элемента");
			Console.WriteLine("6. Удаление элементов");
			Console.WriteLine("0. Выход");
			Console.WriteLine();

			var option = ReadInt("Выбор[0-6]: ");
			DisplayMenu(GetMainMenuEntry(option));
		}

		/// <summary>
		/// Преобразовать выбор пользователя в численное представление меню.
		/// </summary>
		/// <param name="option">Значение выбранное пользователем.</param>
		/// <returns>Меню которое необходимо показать.</returns>
		private static MenuEntry GetMainMenuEntry (int option)
		{
			switch (option)
			{
				case 1:
					return MenuEntry.Create;
				case 2:
					return MenuEntry.Fill;
				case 3:
					return MenuEntry.Display;
				case 4:
					return MenuEntry.Sort;
				case 5:
					return MenuEntry.Аdd;
				case 6:
					return MenuEntry.Remove;
				case 0:
					return MenuEntry.Exit;
				default:
					return MenuEntry.Main;
			}
		}

		/// <summary>
		/// Отобразить меню создания списка.
		/// </summary>
		private static void CreateArrayMenu ()
		{
			Console.WriteLine("Создание Списка\n");

			int size;
			bool hasValidSize;
			do
			{
				// Считываем размер массива до тех пор пока не будет удовлетворять условиям
				size = ReadInt(string.Format("Введите размера списка: "));
				hasValidSize = size > 0;
				if (!hasValidSize) Console.WriteLine("Неверный размер списка!\n");
			} while (!hasValidSize);

			// Создаем и заполняем список
			buffer = new LinkedList(size);
			Fill(buffer);

			// Отображаем элементы списка
			Print(buffer);

			ReadLine("\nНажмите клавишу ввода для возврата в главное меню...");
			DisplayMenu(MenuEntry.Main);
		}

		/// <summary>
		/// Отобразить меню заполнения списка.
		/// </summary>
		private static void FillArrayMenu ()
		{
			Console.WriteLine("Заполнение Списка\n");

			// Заполняем и отображаем элементы массива
			Fill(buffer);
			Print(buffer);

			ReadLine("\nНажмите клавишу ввода для возврата в главное меню...");
			DisplayMenu(MenuEntry.Main);
		}

		/// <summary>
		/// Отобразить меню вывода списка.
		/// </summary>
		private static void PrintArrayMenu ()
		{
			Console.WriteLine("Вывод Массива\n");

			Print(buffer);
			ReadLine("\nНажмите клавишу ввода для возврата в главное меню...");
			DisplayMenu(MenuEntry.Main);
		}

		/// <summary>
		/// Отобразить меню сортировки списка.
		/// </summary>
		private static void SortArrayMenu ()
		{
			Console.WriteLine("Сортировка Списка\n");

			// Засекаем время необходимое на сортировку списка
			var watch = Stopwatch.StartNew();
			buffer.Sort(Comparer<int>.Default);
			watch.Stop();

			// Отображаем элементы списка
			Print(buffer);
			Console.WriteLine("\nОперация сортировки заняла {0} тактов системного таймера.", watch.ElapsedTicks);

			ReadLine("\nНажмите клавишу ввода для возврата в главное меню...");
			DisplayMenu(MenuEntry.Main);
		}

		/// <summary>
		/// Отобразить меню поиска добавления нового элемента в список.
		/// </summary>
		private static void AddArrayMenu ()
		{
			Console.WriteLine("Добавление элемента\n");

			var value = ReadInt(string.Format("Введите значение: "));
			var after = ReadInt(string.Format("Добавить после: "));

			// Определяем куда будет добавлен новый элемент
			Console.WriteLine();
			var node = buffer.Find(after);
			if (node != null) Console.WriteLine("Элемент {0} добавлен после элемента {1}.", value, after);
			else Console.WriteLine("Элемент {0} добавлен в начало списка.", value);

			// Добавляем элемент
			buffer.AddAfter(after, value);
			Print(buffer);

			ReadLine("\nНажмите клавишу ввода для возврата в главное меню...");
			DisplayMenu(MenuEntry.Main);
		}

		/// <summary>
		/// Отобразить меню очистки списка.
		/// </summary>
		private static void RemoveArrayMenu ()
		{
			Console.WriteLine("Удаление элементов\n");
			Console.WriteLine("Удаление из списка всех элементов, которые меньше среднего значения элементов списка.");
			Console.WriteLine();

			// Считаем среднее значение
			var sum = 0;
			foreach (int value in buffer.GetValues())
				sum += value;
			var average = sum / buffer.Count;
			Console.WriteLine("Среднее значение элементов списка: {0}.\n", average);

			// Удаляем все значения который ниже среднего значения
			buffer.RemoveAll((x => (int)x < average));
			Print(buffer);

			ReadLine("\nНажмите клавишу ввода для возврата в главное меню...");
			DisplayMenu(MenuEntry.Main);
		}

		/// <summary>
		/// Заполнить список случайными целыми числами.
		/// </summary>
		public static void Fill (LinkedList array)
		{
			foreach (var node in array.GetNodes())
				node.Value = random.Next(0, array.Count);
		}

		/// <summary>
		/// Вывод элементов списка на экран.
		/// </summary>
		/// <param name="array">Экземпляр списка.</param>
		public static void Print (LinkedList array)
		{
			var index = 0;
			Console.WriteLine("Элементы списка [{0}]:", array.Count);
			foreach (var value in array.GetValues())
				Console.WriteLine("[{0}] = {1}", index++, value);
		}

		/// <summary>
		/// Считать данные введенные пользователем.
		/// </summary>
		/// <param name="message">Приглашение для ввода данных.</param>
		/// <returns>Возвращает текст введенный пользователем.</returns>
		private static string ReadLine (string message)
		{
			Console.Write(message);
			return Console.ReadLine();
		}

		/// <summary>
		/// Считать целое число введенное пользователем.
		/// </summary>
		/// <param name="message">Приглашение для ввода данных.</param>
		/// <returns>Возвращает целое число которое ввел пользователь; в противном случае -1.</returns>
		private static int ReadInt (string message)
		{
			Console.Write(message);
			int retVal;
			if (Int32.TryParse(Console.ReadLine(), out retVal))
				return retVal;
			return -1;
		}

		/// <summary>
		/// Перечисление возможных меню для отображения.
		/// </summary>
		public enum MenuEntry
		{
			Main,
			Create,
			Fill,
			Display,
			Sort,
			Аdd,
			Remove,
			Exit,
		}
	}
}
